/*
 * Copyright 2013-2019 Xia Jun(3979434@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************************************
 *                                                                                     *
 *                        Website : http://www.farsunset.com                           *
 *                                                                                     *
 ***************************************************************************************
 */
package com.workcell.workdata.im.mvc.controller.system;

import com.workcell.workdata.im.annotation.GreenFunction;
import com.workcell.workdata.im.component.event.ImageUploadEvent;
import com.workcell.workdata.im.model.misc.Base64MultipartFile;
import com.workcell.workdata.im.model.response.ResponseEntity;
import com.workcell.workdata.im.model.vo.UploadSignVO;
import com.workcell.workdata.im.service.FileStoreService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.function.BiFunction;

@RestController
@RequestMapping("/file")
@Api(produces = "application/json", tags = "文件存储接口" )
public class FileStoreController  extends ResourceHttpRequestHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger(FileStoreController.class);

	@Resource
	private FileStoreService fileStoreService;

	@Resource
	private ApplicationContext applicationContext;

	@Resource
	private BiFunction<String,String,String> contentTypeParser;

	@ApiOperation(httpMethod = "POST", value = "上传文件")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", required = true,paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "key", value = "文件KEY",required = true, paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "file", value = "文件体",required = true, paramType = "form", dataTypeClass = MultipartFile.class)
	})
	@PostMapping(value = "/{bucket}/{key:.+}")
	@GreenFunction
	public ResponseEntity<Void> upload(@RequestPart("file") MultipartFile file, @PathVariable String bucket, @PathVariable String key) {
		fileStoreService.upload(file, bucket, key);
		return ResponseEntity.ok();
	}


	@ApiOperation(httpMethod = "POST", value = "上传base64")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "key", value = "文件KEY", paramType = "path", dataTypeClass = String.class)
	})
	@PostMapping(value = "/base64/{bucket}/{key:.+}")
	public ResponseEntity<Void> upload(@RequestBody String base64, @PathVariable String bucket, @PathVariable String key) throws IOException {
		fileStoreService.upload(Base64MultipartFile.of(base64), bucket, key);
		return ResponseEntity.ok();
	}


	@ApiOperation(httpMethod = "DELETE", value = "删除文件")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "key", value = "文件KEY", paramType = "path", dataTypeClass = String.class)
	})
	@DeleteMapping(value = "/{bucket}/{key:.+}")
	public ResponseEntity<Void> delete(@PathVariable String bucket, @PathVariable String key) {
		fileStoreService.delete(bucket, key);
		return ResponseEntity.ok();
	}

	/**
	 * 文件下载，支持断点续传
	 */

	@ApiOperation(httpMethod = "GET", value = "下载文件")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "key", value = "文件KEY", paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "name", value = "文件名", paramType = "query", dataTypeClass = String.class)

	})
	@GreenFunction
	@GetMapping(value = "/{bucket}/{key:.+}")
	public void download(@PathVariable String bucket, @PathVariable String key,String name, HttpServletRequest request,
						 HttpServletResponse response) throws IOException, ServletException {

		String fileName = name == null ? key : name;

		org.springframework.core.io.Resource resource = fileStoreService.get(bucket, key);

		if (resource == null) {
			response.sendError(HttpStatus.NOT_FOUND.value());
			return;
		}

		response.setHeader(HttpHeaders.CONTENT_TYPE, contentTypeParser.apply(bucket,fileName));

		/*
		 * 来自OSS的文件地址，重定向到第三方文件地址
		 */
		if (resource instanceof UrlResource) {
			response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
			response.setHeader(HttpHeaders.LOCATION,resource.getURL().toString());
			return;
		}

		/*
		 * 来自本机文件，则使用spring mvc 自带的文件下载组件处理
		 */
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));

		response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()));

		request.setAttribute("resource", resource);

		super.handleRequest(request, response);

	}

	@Override
	protected org.springframework.core.io.Resource getResource(HttpServletRequest request) {
		return (org.springframework.core.io.Resource) request.getAttribute("resource");
	}

	@ApiOperation(httpMethod = "POST", value = "添加引用计数")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", paramType = "path", dataTypeClass = String.class),
			@ApiImplicitParam(name = "key", value = "文件KEY", paramType = "path", dataTypeClass = String.class)
	})
	@PostMapping(value = "/count/{bucket}/{key:.+}")
	public ResponseEntity<Void> count(@PathVariable String bucket, @PathVariable String key) {
		fileStoreService.addReferenceCount(bucket, key);
		return ResponseEntity.ok();
	}


	@ApiOperation(httpMethod = "GET", value = "获取客户端直传签名")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "bucket", value = "文件库名", paramType = "query", dataTypeClass = String.class),
			@ApiImplicitParam(name = "name", value = "文件名称", paramType = "query", dataTypeClass = String.class)
	})
	@GetMapping(value = "/upload/sign")
	public ResponseEntity<UploadSignVO> getUploadSign(@RequestParam String bucket, @RequestParam String name) {
		return ResponseEntity.ok(fileStoreService.getUploadSign(bucket,name));
	}


	@ApiIgnore
	@ApiOperation(httpMethod = "POST", value = "OSS上传完成回调")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "filename", value = "文件名", paramType = "query", dataTypeClass = String.class),
			@ApiImplicitParam(name = "mimeType", value = "文件类型", paramType = "query", dataTypeClass = String.class)
	})
	@PostMapping(value = "/cloud/storage/callback")
	@GreenFunction
	public ResponseEntity<Void> onUploadCallback(@RequestParam String filename,@RequestParam long size,@RequestParam String mimeType) {

		String displaySize = FileUtils.byteCountToDisplaySize(size);

		LOGGER.info("OSS文件上传成功回调,filename:{} mimeType:{} size:{}",filename,mimeType, displaySize);

		if (mimeType.startsWith("image/")){
			String bucket = filename.split("/")[0];
			String name   = filename.split("/")[1];
			applicationContext.publishEvent(new ImageUploadEvent(bucket,name));
		}
		return ResponseEntity.ok();
	}
}
